﻿using UnityEngine;

#if UNITY_EDITOR
#endif

namespace CWM.Skinn
{
    public static partial class SkinnEx
    {
        /// <summary>
        /// Instantiated
        /// </summary>
        public static SkinnedMeshRenderer ChangeBoneArray(this SkinnedMeshRenderer source, Transform rootBone, bool bake = false)
        {
            if (!source || !source.HasMinimumRequirements(true)) return null;

            GameObject smrGo = new GameObject(source.name);
            SkinnedMeshRenderer smr = smrGo.AddComponent<SkinnedMeshRenderer>();
            
            Mesh mesh = GameObject.Instantiate(source.sharedMesh);

            if (bake)
            {
                Mesh bakeMesh = new Mesh();
                source.BakeMesh(bakeMesh);
                mesh.vertices = bakeMesh.vertices;
                mesh.normals = bakeMesh.normals;

                if (Application.isEditor) GameObject.DestroyImmediate(bakeMesh);
                else GameObject.Destroy(bakeMesh);
            }

            Transform[] bones = rootBone.GetAllChildren(true).ToArray();


            smr.sharedMesh = mesh;
            smr.sharedMesh.name = source.sharedMesh.name == null ? source.name : source.sharedMesh.name;
            smr.rootBone = rootBone;
            smr.bones = bones;
            smr.localBounds = mesh.bounds;
            smr.updateWhenOffscreen = source.updateWhenOffscreen;
            smr.quality = source.quality;

            Matrix4x4[] bindposes = new Matrix4x4[bones.Length];

            for (int i = 0; i < bones.Length; i++)
            {
                if (bones[i] == smr.rootBone)
                    bindposes[i] = bones[i].worldToLocalMatrix * smr.rootBone.transform.root.localToWorldMatrix;
                else
                    bindposes[i] = bones[i].worldToLocalMatrix;
            }

            smr.sharedMesh.bindposes = bindposes;

            Transform[] transforms = source.bones;

            int[] transformHashes = transforms.GetOrderedHashes();
            int[] boneHashes = bones.GetOrderedHashes();

            BoneWeight[] boneWeights = mesh.boneWeights;
            BoneWeight[] newBoneWeights = new BoneWeight[mesh.vertices.Length];
            for (int i = 0; i < boneWeights.Length; i++)
            {
                BoneWeight boneWeight = new BoneWeight
                {
                    boneIndex0 = boneWeights[i].boneIndex0,
                    boneIndex1 = boneWeights[i].boneIndex1,
                    boneIndex2 = boneWeights[i].boneIndex2,
                    boneIndex3 = boneWeights[i].boneIndex3,
                    weight0 = boneWeights[i].weight0,
                    weight1 = boneWeights[i].weight1,
                    weight2 = boneWeights[i].weight2,
                    weight3 = boneWeights[i].weight3
                };
                
                if (boneWeight.weight0 > 0) boneWeight.boneIndex0 = boneHashes.GetIndex(transformHashes[boneWeight.boneIndex0], false);
                if (boneWeight.weight1 > 0) boneWeight.boneIndex1 = boneHashes.GetIndex(transformHashes[boneWeight.boneIndex1], false);
                if (boneWeight.weight2 > 0) boneWeight.boneIndex2 = boneHashes.GetIndex(transformHashes[boneWeight.boneIndex2], false);
                if (boneWeight.weight3 > 0) boneWeight.boneIndex3 = boneHashes.GetIndex(transformHashes[boneWeight.boneIndex3], false);

                newBoneWeights[i] = boneWeight;
            }

            smr.sharedMesh.boneWeights = newBoneWeights;
            smr.sharedMaterials = source.sharedMaterials;

            smr.transform.SetParent(source.transform.parent);
            source.gameObject.SetActive(false);

            return smr;
        }

    }
}
